use crate::config::app_error::{AppError, AppResult};
use crate::extension::index::index::key_comparator;
use crate::extension::index::index::query::index_attribute::IndexAttribute;
use crate::extension::index::index::query::index_descriptor::IndexDescriptor;
use crate::extension::index::index::query::index_spec::OrderType;
use halo_model::{ExtensionGVK, ExtensionOperator, GVK};
use ordermap::{OrderMap, OrderSet};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::Hash;

/// IndexEntry trait 用于存储索引键和 Metadata::get_name() 之间的映射。
/// IndexEntry used to store the mapping between index key and Metadata.getName().
/// For example, if we have a Metadata with name foo and labels bar=1 and baz=2, then the index entry will be:
///       bar=1 -> foo
///       baz=2 -> foo
///
/// And if we have another Metadata with name bar and labels bar=1 and baz=3, then the index entry will be:
///       bar=1 -> foo, bar
///       baz=2 -> foo
///       baz=3 -> bar
///
/// getIndexDescriptor() describes the owner of this index entry.
/// Index entries is ordered by key, and the order is determined by IndexSpec.getOrder().
/// Do not modify the returned result for all methods of this class.
//// This class is thread-safe.
pub trait IndexEntry {
    type T: ExtensionGVK;
    /// 添加新的索引项。
    /// Adds a new entry to this index entry.
    /// For example, if we have a Metadata with name foo and labels bar=1 and baz=2 and index order is IndexSpec. OrderType. ASC, then the index entry will be:
    /// bar=1 -> foo
    /// baz=2 -> foo
    fn add_entry(&mut self, index_keys: Vec<String>, object_key: String) -> AppResult<()>;

    /// 移除给定索引键和对象键的项。
    fn remove_entry(&mut self, indexed_key: String, object_key: String);

    /// 移除给定对象键的所有项。
    fn remove(&mut self, object_key: String);

    /// 返回此条目的 IndexDescriptor。
    fn get_index_descriptor(&self) -> &IndexDescriptor<Self::T>;

    fn get_inner_index_descriptor(self) -> IndexDescriptor<Self::T>;
    /// 返回此条目的索引键，按顺序排列。
    fn indexed_keys(&self) -> OrderSet<String>;

    /// 返回此条目的条目，按顺序排列。
    /// 注意：对返回结果的任何修改都直接影响原始数据。
    fn entries(&self) -> impl Iterator<Item = (&String, &String)>;

    /// 返回索引属性值映射中对象名称的排序位置。
    /// Returns the position of the object name in the indexed attribute value mapping for sorting.
    /// For example:
    /// metadata. name | field1
    /// ------------- | ------
    /// foo           | 1
    /// bar           | 2
    /// baz           | 2
    ///
    /// "field1" is the indexed attribute, and the position of the object name in the indexed attribute value mapping for sorting is:
    /// foo -> 0
    /// bar -> 1
    /// baz -> 1
    ///
    /// "bar" and "baz" have the same value, so they have the same position.
    fn get_id_position_map(&self) -> HashMap<String, i32>;

    /// 根据索引键返回此条目的对象名称，按顺序排列。
    fn get_object_names_by(&self, index_key: &str) -> Vec<String>;

    /// 清除所有条目。
    fn clear(&mut self);
}

pub struct IndexEntryImpl<T>
where
    T: ExtensionGVK,
{
    index_key_object_names_map: OrderMap<String, Vec<String>>,
    index_descriptor: IndexDescriptor<T>,
}

impl<T> IndexEntryImpl<T>
where
    T: ExtensionGVK,
{
    pub fn new(index_descriptor: IndexDescriptor<T>) -> Self {
        IndexEntryImpl {
            index_descriptor,
            index_key_object_names_map: OrderMap::new(),
        }
    }

    fn key_comparator(&self, a: &str, b: &str) -> Ordering {
        let order = &self.index_descriptor.spec.order;
        match order {
            OrderType::ASC => key_comparator::compare(a, b),
            OrderType::DESC => key_comparator::compare(b, a),
        }
    }
    pub fn set_ready(&mut self, ready: bool) {
        self.index_descriptor.ready = ready;
    }
}

impl<T: ExtensionGVK> IndexEntry for IndexEntryImpl<T> {
    type T = T;

    fn add_entry(&mut self, index_keys: Vec<String>, object_key: String) -> AppResult<()> {
        let unique = self.index_descriptor.spec.unique;

        for key in index_keys {
            if unique && self.index_key_object_names_map.contains_key(&key) {
                return Err(AppError::DuplicateNameError(format!(
                    "The value [{}] is already exists for unique index [{}].",
                    key, self.index_descriptor.spec.name
                )));
            }
            match self.index_key_object_names_map.get_mut(&key) {
                Some(vec) => vec.push(object_key.clone()),
                None => {
                    self.index_key_object_names_map
                        .insert(key, vec![object_key.clone()]);
                }
            };
        }
        Ok(())
    }

    fn remove_entry(&mut self, indexed_key: String, object_key: String) {
        match self.index_key_object_names_map.get_mut(&indexed_key) {
            None => {}
            Some(vec) => {
                vec.retain(|x| *x != object_key);
                if vec.is_empty() {
                    self.index_key_object_names_map.remove(&indexed_key);
                }
            }
        }
    }

    fn remove(&mut self, object_key: String) {
        // 创建一个临时变量来存储需要删除的键
        let mut keys_to_remove = Vec::new();

        // 遍历 HashMap 的所有键值对
        for (index_key, object_names) in &mut self.index_key_object_names_map {
            // 使用 `retain` 移除所有等于 `object_key` 的元素
            object_names.retain(|x| object_key.eq(x));

            // 如果向量变为空，则记录需要删除的键
            if object_names.is_empty() {
                keys_to_remove.push(index_key.clone());
            }
        }

        // 在遍历完成后，再删除记录下来的键
        for key in keys_to_remove {
            self.index_key_object_names_map.remove(&key);
        }
    }

    fn get_index_descriptor(&self) -> &IndexDescriptor<T> {
        &self.index_descriptor
    }
    fn get_inner_index_descriptor(self) -> IndexDescriptor<T> {
        self.index_descriptor
    }

    fn indexed_keys(&self) -> OrderSet<String> {
        let mut set: Vec<String> = self.index_key_object_names_map.keys().cloned().collect();
        set.sort_by(|a, b| self.key_comparator(a, b));
        let mut order_set = OrderSet::new();
        order_set.extend(set);
        order_set
    }

    fn entries(&self) -> impl Iterator<Item = (&String, &String)> {
        self.index_key_object_names_map
            .iter()
            .flat_map(|(key, values)| values.iter().map(move |value| (key, value)))
    }

    fn get_id_position_map(&self) -> HashMap<String, i32> {
        let mut id_position_map = HashMap::new();
        let values = self.index_key_object_names_map.values();
        let mut i = 0;

        for value in values {
            for v in value {
                id_position_map.insert((*v).clone(), i);
            }
            i = i + 1;
        }
        id_position_map
    }

    fn get_object_names_by(&self, index_key: &str) -> Vec<String> {
        self.index_key_object_names_map
            .get(index_key)
            .map_or(vec![], |v| v.clone())
    }

    fn clear(&mut self) {
        self.index_key_object_names_map.clear();
    }
}

#[cfg(test)]
mod tests {
    use std::vec;

    use crate::{
        extension::index::index::query::query::QueryIndexView,
        tests::{
            contains_entry,
            index_view_data_set::{self, create_comment_index_view, FakeExtension, MockIndexer},
            primary_key_index_spec,
        },
    };

    use super::*;

    #[test]
    fn add() {
        let spec = primary_key_index_spec();

        let descriptor = IndexDescriptor::new(spec);

        let mut index_entry = IndexEntryImpl::new(descriptor);

        index_entry
            .add_entry(vec!["slug-1".to_string()], "fake-name-1".to_string())
            .unwrap();

        assert!(index_entry.indexed_keys().contains("slug-1"));
    }

    #[test]
    fn remove() {
        let spec = primary_key_index_spec();
        let descriptor = IndexDescriptor::new(spec);
        let mut index_entry = IndexEntryImpl::new(descriptor);
        index_entry
            .add_entry(vec!["slug-1".to_string()], "fake-name-1".to_string())
            .unwrap();
        assert!(index_entry.indexed_keys().contains("slug-1"));
        assert_eq!(index_entry.entries().count(), 1);
        index_entry.remove_entry("slug-1".to_string(), "fake-name-1".to_string());
        assert!(index_entry.indexed_keys().is_empty());
        assert_eq!(index_entry.entries().count(), 0);
    }

    #[test]
    fn remove_by_index() {
        let spec = primary_key_index_spec();
        let descriptor = IndexDescriptor::new(spec);
        let mut index_entry = IndexEntryImpl::new(descriptor);
        index_entry
            .add_entry(
                vec!["slug-1".to_string(), "slug-2".to_string()],
                "fake-name-1".to_string(),
            )
            .unwrap();
        assert!(index_entry.indexed_keys().contains("slug-1"));
        assert!(index_entry.indexed_keys().contains("slug-2"));
        assert_eq!(index_entry.entries().count(), 2);
        index_entry.remove("fake-name-1".to_string());
        assert!(index_entry.indexed_keys().is_empty());
        assert_eq!(index_entry.entries().count(), 0);
    }

    #[test]
    fn get_object_ids_test() {
        let spec = primary_key_index_spec();
        let descriptor = IndexDescriptor::new(spec);
        let mut index_entry = IndexEntryImpl::new(descriptor);
        index_entry
            .add_entry(
                vec!["slug-1".to_string(), "slug-2".to_string()],
                "fake-name-1".to_string(),
            )
            .unwrap();
        assert!(index_entry.indexed_keys().contains("slug-1"));
        assert!(index_entry.indexed_keys().contains("slug-2"));
        assert_eq!(index_entry.entries().count(), 2);
        assert_eq!(
            index_entry.get_object_names_by("slug-1"),
            vec!["fake-name-1".to_string()]
        );
    }

    #[test]
    fn key_order() {
        let mut spec = primary_key_index_spec();
        spec.order = OrderType::DESC;
        let descriptor = IndexDescriptor::new(spec);
        let mut index_entry = IndexEntryImpl::new(descriptor);
        index_entry
            .add_entry(
                vec!["slug-1".to_string(), "slug-2".to_string()],
                "fake-name-1".to_string(),
            )
            .unwrap();
        index_entry
            .add_entry(vec!["slug-3".to_string()], "fake-name-2".to_string())
            .unwrap();

        index_entry
            .add_entry(vec!["slug-4".to_string()], "fake-name-2".to_string())
            .unwrap();

        let indexed_keys = index_entry.indexed_keys();
        assert!(indexed_keys.contains(&"slug-4".to_string()));
        assert!(indexed_keys.contains(&"slug-3".to_string()));
        assert!(indexed_keys.contains(&"slug-2".to_string()));
        assert!(indexed_keys.contains(&"slug-1".to_string()));

        let mut spec = primary_key_index_spec();
        spec.order = OrderType::ASC;

        let descriptor2 = IndexDescriptor::new(spec);

        let mut index_entry2 = IndexEntryImpl::new(descriptor2);
        index_entry2
            .add_entry(
                vec!["slug-1".to_string(), "slug-2".to_string()],
                "fake-name-1".to_string(),
            )
            .unwrap();
        index_entry2
            .add_entry(vec!["slug-3".to_string()], "fake-name-2".to_string())
            .unwrap();
        index_entry2
            .add_entry(vec!["slug-4".to_string()], "fake-name-3".to_string())
            .unwrap();

        assert!(contains_entry(
            index_entry2.entries(),
            "slug-1",
            "fake-name-1"
        ));
        assert!(contains_entry(
            index_entry2.entries(),
            "slug-2",
            "fake-name-1"
        ));
        assert!(contains_entry(
            index_entry2.entries(),
            "slug-3",
            "fake-name-2"
        ));
        assert!(contains_entry(
            index_entry2.entries(),
            "slug-4",
            "fake-name-3"
        ));

        let indexed_keys = index_entry2.indexed_keys();
        indexed_keys.contains(&"slug-1".to_string());
        indexed_keys.contains(&"slug-2".to_string());
        indexed_keys.contains(&"slug-3".to_string());
        indexed_keys.contains(&"slug-4".to_string());
    }

    #[test]
    fn get_id_position_map_test() {
        let index_view = create_comment_index_view();

        let top_index_entry = prepare_for_position_map_test(&index_view, "spec.top");
        let top_index_entry_from_view = index_view
            .get_index_entry::<FakeExtension>("spec.top")
            .unwrap();

        top_index_entry_from_view.borrow_mut().get_id_position_map();

        assert_eq!(
            top_index_entry.get_id_position_map(),
            top_index_entry_from_view.borrow_mut().get_id_position_map()
        );
        let creation_time_index_entry =
            prepare_for_position_map_test(&index_view, "spec.creationTime");
        let creation_time_index_entry_from_view =
            index_view.get_index_entry("spec.creationTime").unwrap();
        assert_eq!(
            creation_time_index_entry.get_id_position_map(),
            creation_time_index_entry_from_view
                .borrow_mut()
                .get_id_position_map()
        );
        let priority_index_entry = prepare_for_position_map_test(&index_view, "spec.priority");
        let priority_index_entry_from_view = index_view.get_index_entry("spec.priority").unwrap();
        assert_eq!(
            priority_index_entry.get_id_position_map(),
            priority_index_entry_from_view
                .borrow_mut()
                .get_id_position_map()
        );
    }
    fn prepare_for_position_map_test(
        index_view: &QueryIndexView<MockIndexer<FakeExtension>>,
        property: &str,
    ) -> IndexEntryImpl<FakeExtension> {
        let spec = primary_key_index_spec();

        let index_descriptor = IndexDescriptor::new(spec);

        let index_entry_from_view = index_view.get_index_entry(&property.to_string()).unwrap();
        let mut vec = Vec::new();
        for (ele, ele2) in index_entry_from_view.borrow_mut().entries() {
            vec.push((ele.clone(), ele2.clone()));
        }
        index_view_data_set::sort_entries(&mut vec);

        let map = index_view_data_set::to_key_object_map(&vec);

        let index_entry = IndexEntryImpl {
            index_key_object_names_map: map,
            index_descriptor,
        };

        index_entry
    }
}
